import 'dart:io';
import 'dart:math';

import 'package:filelinker/netsev.dart';
import 'package:flutter/material.dart';
import 'package:nsd/nsd.dart';
import 'package:device_info_plus/device_info_plus.dart';

class MDNS {
  static MDNS shared = MDNS();

  final String _serviceType = "_http._tcp";
  int port = 58090;
  final String _serviceName = "FileLinker";
  late Service _service;
  final List<Service> devices = [];
  String name = "Unknown";
  bool isDesktop = false;

  VoidCallback? onDeviceFound;

  MDNS() {
    if (Platform.isMacOS) {
      port = Random().nextInt(9999) + 40000;
    } else if (Platform.isAndroid) {
      port = Random().nextInt(9999) + 50000;
    } else if (Platform.isIOS) {
      port = Random().nextInt(9999) + 60000;
    } else {
      port = Random().nextInt(9999) + 70000;
    }
    isDesktop = Platform.isMacOS || Platform.isWindows || Platform.isLinux;
    NetService.shared.start(port);
  }

  Discovery? _discovery;
  Registration? _registration;
  String _localName = "unknown";

  Future<void> _getDeviceInfo() async {
    _localName = Platform.localHostname;
    debugPrint("local name: $_localName");
    final deviceInfo = DeviceInfoPlugin();
    if (Platform.isAndroid) {
      name = (await deviceInfo.androidInfo).model;
    } else if (Platform.isIOS) {
      final dev = await deviceInfo.iosInfo;
      name = "${dev.model} ${dev.systemName} ${dev.systemVersion}";
    } else if (Platform.isLinux) {
      name = (await deviceInfo.linuxInfo).name;
    } else if (Platform.isMacOS) {
      name = (await deviceInfo.macOsInfo).computerName;
    } else if (Platform.isWindows) {
      name = (await deviceInfo.windowsInfo).computerName;
    }
    debugPrint("device name: $name");
  }

  void start() async {
    await _getDeviceInfo();
    _service = Service(
      name: "$_serviceName $name",
      type: _serviceType,
      port: port,
    );
    await _registerService();
    _lookupService();
  }

  void stop() async {
    if (_discovery != null) {
      await stopDiscovery(_discovery!);
    }
    if (_registration != null) {
      await unregister(_registration!);
    }
    debugPrint("stop");
  }

  void _lookupService() async {
    _discovery = await startDiscovery(_serviceType, ipLookupType: IpLookupType.v4);
    _discovery?.addServiceListener((service, status) {
      _addDevice(service, status == ServiceStatus.lost);
    });
    enableLogging(LogTopic.calls);
  }

  void _addDevice(Service service, bool isLost) {
    if (service.name?.contains("FileLinker") == false) return;
    if (service.host?.contains(_localName) == true) return;
    if (service.name?.contains(name) == true) return;
    var contained = devices.any((e) => e.host == service.host);
    if (isLost && contained) {
      devices.remove(service);
    } else if (!contained && !isLost) {
      devices.add(service);
    }
    debugPrint("${isLost ? "Lost " : "Found "}device: $service");
    if (onDeviceFound != null) {
      onDeviceFound!();
    }
  }

  Future<void> _registerService() async {
    _registration = await register(_service);
    debugPrint(_registration.toString());
  }
}
